package com.lmk.mkcc.service.impl;

import com.alibaba.fastjson.JSON;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.AlipayTradeRefundResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.lmk.mkcc.domain.AlipayInfo;
import com.lmk.mkcc.domain.PayFlow;
import com.lmk.mkcc.domain.PayOrder;
import com.lmk.mkcc.dto.AlipayNotifyDto;
import com.lmk.mkcc.dto.PayNotifyLocalTxParamDto;
import com.lmk.mkcc.dto.PayParamDto;
import com.lmk.mkcc.enums.GlobalErrorCode;
import com.lmk.mkcc.exception.MyGlobalCustomException;
import com.lmk.mkcc.result.JSONResult;
import com.lmk.mkcc.service.IAlipayInfoService;
import com.lmk.mkcc.service.IPayOrderService;
import com.lmk.mkcc.service.IPayService;
import com.lmk.mkcc.util.AssertUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

@Slf4j
@Service
public class PayServiceImpl implements IPayService {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private IPayOrderService payOrderService;
    @Autowired
    private IAlipayInfoService alipayInfoService;
    @Override
    public JSONResult checkPayOrder(String orderNo) {
        // 判断orderNo在支付单中是否存在
        PayOrder byOrderNo = payOrderService.getByOrderNo(orderNo);
        return byOrderNo!=null?JSONResult.success():JSONResult.error();
    }

    @Override
    public String apply(PayParamDto payParamDto) {
        log.info("开启支付了，订单编号是：%s",payParamDto.getOrderNo());
        // 非空参数判断
        PayOrder payOrder = payOrderService.getByOrderNo(payParamDto.getOrderNo());
        AssertUtil.isNotNull(payOrder, GlobalErrorCode.NO_PAYORSER_ERROR);
        // 查询订单状态
        boolean b1 = payOrder.getPayStatus()==PayOrder.WAIT_PAY;
        AssertUtil.isTrue(b1,GlobalErrorCode.STATUS_ERROR);

        // 支付方式:0余额直接，1支付宝，2微信,3银联
        if (payParamDto.getPayType() == 1){
            log.info("开始调用支付宝接口");
            String callUrl = payParamDto.getCallUrl();
            return alipay(payOrder,callUrl);
        }
        return null;
    }

/*--------------------------异步回调接口方法实现-------------------------------------------------------*/
    @Override
    public String alipayNotify(AlipayNotifyDto alipayNotifyDto) {
        try {
            Map map = JSON.parseObject(JSON.toJSONString(alipayNotifyDto), Map.class);
            // 1.进行支付宝验签
            Boolean aBoolean = Factory.Payment.Common().verifyNotify(map);
            if (aBoolean==false){
                return "success";
            }
            // 2.判断支付包支付状态
            if(!alipayNotifyDto.getTrade_status().equals(AlipayNotifyDto.TRADE_SUCCESS)){
                return "success";
            }
            // 3.通过订单号查询支付单是否存在
            String out_trade_no = alipayNotifyDto.getOut_trade_no();
            PayOrder byOrderNo = payOrderService.getByOrderNo(out_trade_no);
            if (byOrderNo==null){
                return "success";
            }
            // 4.判断的支付金额是否正确
            if (byOrderNo.getAmount().compareTo(new BigDecimal(alipayNotifyDto.getTotal_amount()))!= 0){
                return "success";
            }
            // 5.如果支付单状态为取消时，进行退款操作
            if(PayOrder.CANCEL_PAY.equals(byOrderNo.getPayStatus()) || PayOrder.FAIL_PAY.equals(byOrderNo.getPayStatus())){
                try {
                    // 调用支付宝退款接口,传入订单号和总计金额
                    AlipayTradeRefundResponse refund = Factory.Payment.Common().refund(out_trade_no, alipayNotifyDto.getTotal_amount());
                    if (!refund.getCode().equals("10000")){
                        // 添加业务校验
                    }else {
                        // 退款成功，单独处理支付单和订单流水
                        return "success";
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                    // 添加业务校验
                }
            }
            // 6.如果我们的状态是已经完成了，那么直接响应成功
            if (PayOrder.PAY_SUCCESS.equals(byOrderNo.getPayStatus())){
                return "success";
            }
            // 7.准备事务消息，本地事务消息
            PayNotifyLocalTxParamDto payNotifyLocalTxParamDto = PrepareLocalTransactions(byOrderNo,alipayNotifyDto);
            // 7.1.准备arg
            String arg = JSON.toJSONString(payNotifyLocalTxParamDto);
            // 7.2.封装message
            Message<String> message = MessageBuilder.withPayload(byOrderNo.getExtParams()).build();
            // 8.mq事务消息发送，向课程服务和订单服务发送本地事务消息，修改订单状态
            TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
                    "pay-transaction-group",
                    "pay-transaction-topic:pay-transaction-tag",
                    message,
                    arg
            );
            // 8.1判断事务是否发送成功
            boolean sendOk =SendStatus.SEND_OK == transactionSendResult.getSendStatus();
            if (!sendOk){
                // 相关业务处理。请联系管理员
                return "fail";
            }

        } catch (Exception e) {
            e.printStackTrace();
            return "fail";
        }
        return "success";
    }

    private PayNotifyLocalTxParamDto PrepareLocalTransactions(PayOrder byOrderNo, AlipayNotifyDto alipayNotifyDto) {
        try {
            // 准备保存到流水单表
            PayFlow payFlow = new PayFlow();
            payFlow.setCode(alipayNotifyDto.getCode());
            payFlow.setMsg(alipayNotifyDto.getMsg());
            SimpleDateFormat simpleFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            payFlow.setNotifyTime(simpleFormatter.parse(alipayNotifyDto.getNotify_time()));

            payFlow.setOutTradeNo(alipayNotifyDto.getOut_trade_no());
            payFlow.setPassbackParams(alipayNotifyDto.getPassback_params());
            payFlow.setPaySuccess(true);
            payFlow.setResultDesc(alipayNotifyDto.getBody());
            payFlow.setSubject(alipayNotifyDto.getSubject());
            payFlow.setTotalAmount(new BigDecimal(alipayNotifyDto.getTotal_amount()));
            payFlow.setTradeStatus(alipayNotifyDto.getTrade_status());
            // 修改支付单信息
            byOrderNo.setPayStatus(PayOrder.PAY_SUCCESS);
            byOrderNo.setUpdateTime(new Date());

            return new PayNotifyLocalTxParamDto(payFlow, byOrderNo);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /*-----------------------------------------------------------------------------------------------*/
    private String alipay(PayOrder payOrder, String callUrl) {
        AlipayInfo alipayInfo = alipayInfoService.selectOne(null);
        // 1. 设置参数（全局只需设置一次）
        Factory.setOptions(getOptions(alipayInfo));
        try {
            // 2. 发起API调用,网页支付easy版
            AlipayTradePagePayResponse response = Factory.Payment.Page().pay(payOrder.getSubject(),payOrder.getOrderNo(),payOrder.getAmount().toString(), callUrl);
            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                log.info("调用支付宝成功!");
                return response.getBody();
            }
        } catch (Exception e) {
            log.info("调用遭遇异常，原因：" + e.getMessage());
            throw new MyGlobalCustomException(GlobalErrorCode.ERROE);
        }
        return null;
    }

    private Config getOptions(AlipayInfo alipayInfo) {
        Config config = new Config();
        config.protocol = alipayInfo.getProtocol();
        config.gatewayHost = alipayInfo.getGatewayHost();
        config.signType = alipayInfo.getSignType();
        config.appId = alipayInfo.getAppId();
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = alipayInfo.getMerchantPrivateKey();
        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = alipayInfo.getAlipayPublicKey();
        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = alipayInfo.getNotifyUrl();
        return config;
    }

}
